﻿using System;
using System.Collections.Generic;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.InterceptionExtension;

namespace cludg.Infrastructure.DI.Unity
{
	using Core.Attributes;

	public class DIContainer : IDIContainer
	{
		protected readonly IUnityContainer Container = new UnityContainer();

		public DIContainer()
		{
			Container.AddNewExtension<Enumerable.EnumerableExtension>();
			Container.AddNewExtension<Interception>();
			Container.AddNewExtension<DependecyAttribute.DepedencyAttributeExtension>();
		
			Container.RegisterType<ILogInvokeHandler, LogInvokeHandler>(new ContainerControlledLifetimeManager());

			Container.RegisterType<InjectionPolicy, UniversalAttributeInjectionPolicy>(
				typeof(UniversalAttributeInjectionPolicy).AssemblyQualifiedName,
				new ContainerControlledLifetimeManager());
		}

		private static LifetimeManager GetLifetimeManager(DILifetime lifetime)
		{
			switch (lifetime)
			{
				case DILifetime.PerResolve:
					return new PerResolveLifetimeManager();
				case DILifetime.PerThread:
					return new PerThreadLifetimeManager();
				case DILifetime.Singleton:
					return new ContainerControlledLifetimeManager();
				default:
					return null;
			}
		}

		private static InjectionMember[] GetInjectionMembers(params object[] constructorParams)
		{
			if ((constructorParams != null) && (constructorParams.Length > 0))
				return new InjectionMember[] { new InjectionConstructor(constructorParams) };
			else
				return new InjectionMember[0];
		}

		public void Register(Type impl, DILifetime lifetime, params object[] constructorParams)
		{
			Container.RegisterType(impl, GetLifetimeManager(lifetime), GetInjectionMembers(constructorParams));
		}

		public void Register(Type intf, Type impl, string name, DILifetime lifetime, params object[] constructorParams)
		{
			Container.RegisterType(intf, impl, name, GetLifetimeManager(lifetime), GetInjectionMembers(constructorParams));

			// TODO: отключать
			Container.Configure<Interception>().SetDefaultInterceptorFor(intf, new TransparentProxyInterceptor());
		}

		public void RegisterInstance(Type intf, object instance, string name)
		{
			Container.RegisterInstance(intf, name, instance, GetLifetimeManager(DILifetime.Singleton));

			// TODO: отключать
			try
			{
				Container.Configure<Interception>().SetDefaultInterceptorFor(intf, new TransparentProxyInterceptor());
			}
			catch
			{
				// TODO: Пока просто отлавливаетм исключения для не перехватываемых типов, но надо проверять
			}

		}

		public object Resolve(Type intf, string name)
		{
			return Container.Resolve(intf, name);
		}

		public IEnumerable<object> ResolveAll(Type intf)
		{
			return Container.ResolveAll(intf);
		}
	}
}
